﻿using System;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;

namespace Gems
{
    [Serializable]
    public class BoolVector
    {
        private readonly UInt64[] _data;
        private readonly int _length;

        public int Length
        {
            get { return _length; }
        }

        public BoolVector(int n)
        {
            _length = n;
            _data = new UInt64[(long)Math.Ceiling(n / 64d)];
        }

        private BoolVector(int n, UInt64[] data)
        {
            _length = n;
            _data = data;
        }

        public bool this[int n]
        {
            get
            {
                Contract.Requires<ArgumentOutOfRangeException>(n < Length);
                var a = n/64;
                var b = n%64;
                return (_data[a] & ((ulong)1 << b)) > 0;
            }

            set
            {
                Contract.Requires<ArgumentOutOfRangeException>(n < Length);
                var a = n / 64;
                var b = n % 64;

                if (value)
                {
                    _data[a] |= ((ulong)1 << b);
                }
                else
                {
                    _data[a] &= ((ulong)1 << b)^0xFFFFFFFFFFFFFFFF;
                }
            }
        }

        public override string ToString()
        {
            var sb = new StringBuilder();

            for (int i = 0; i < Length; i++)
            {
                if (this[i]) sb.Append("1");
                else sb.Append("0");
            }
            return sb.ToString();
        }


        public BoolVector Clone()
        {
            return new BoolVector(_length, (ulong[])_data.Clone());
        }

        public bool Equals(BoolVector other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return _data.SequenceEqual(other._data) && other._length == _length;
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj)) return false;
            if (ReferenceEquals(this, obj)) return true;
            if (obj.GetType() != typeof (BoolVector)) return false;
            return Equals((BoolVector) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((_data != null ? _data.GetHashCode() : 0)*397) ^ _length;
            }
        }
    }
}
